/*
 * Copyright (c) 2020 pig4cloud Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.pig4cloud.pig.ads.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dy.yunying.api.entity.AdRoleUser;
import com.dy.yunying.api.feign.RemoteAdRoleUserService;
import com.google.common.collect.Lists;
import com.pig4cloud.pig.ads.pig.mapper.AdAccountMapper;
import com.pig4cloud.pig.ads.pig.mapper.AdvMapper;
import com.pig4cloud.pig.ads.pig.mapper.AdvertiserMapper;
import com.pig4cloud.pig.ads.pig.mapper.PlatformJobInfoMapper;
import com.pig4cloud.pig.ads.pig.mapper.gdt.GdtAdvertiserMapper;
import com.pig4cloud.pig.ads.service.AdUserAdverService;
import com.pig4cloud.pig.ads.service.AdvService;
import com.pig4cloud.pig.ads.service.AdvertiserService;
import com.pig4cloud.pig.ads.service.TtAccesstokenService;
import com.pig4cloud.pig.ads.utils.CronUtils;
import com.pig4cloud.pig.ads.utils.OEHttpUtils;
import com.pig4cloud.pig.api.entity.*;
import com.pig4cloud.pig.common.core.constant.SecurityConstants;
import com.pig4cloud.pig.common.core.constant.enums.PlatformTypeEnum;
import com.pig4cloud.pig.common.core.util.DateUtil;
import com.pig4cloud.pig.common.core.util.R;
import com.pig4cloud.pig.common.security.service.PigUser;
import com.pig4cloud.pig.common.security.util.SecurityUtils;
import com.pig4cloud.pig.api.dto.BudgetDto;
import com.pig4cloud.pig.api.enums.StatusEnum;
import com.pig4cloud.pig.api.util.Constants;
import com.pig4cloud.pig.api.vo.NameValuePairVo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;


/**
 * @广告账户 服务实现类
 * @author john
 *
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class AdvServiceImpl extends ServiceImpl<AdvMapper, Advertising> implements AdvService {

	private final AdUserAdverService adUserAdverService;

	private final TtAccesstokenService ttAccesstokenService;

	private final StringRedisTemplate stringRedisTemplate;

	private final AdvMapper advMapper;

	private final PlatformJobInfoMapper platformJobInfoMapper;

	private final AdAccountMapper adAccountMapper;

	private final RemoteAdRoleUserService remoteAdRoleUserService;

	@Autowired
	private AdvertiserService advertiserService;

	@Autowired
	private AdvertiserMapper advertiserMapper;


	@Value("${ad_third_url}")
	private String adThirdUrl;
	@Value("${budget_update_url}")
	private String budgetUrl;





	@Override
	public Boolean removeAdvById(Integer id) {
		return baseMapper.deleteAdvById(id) > 0;
	}

	@Override
	public List<NameValuePairVo> options4AdAccounts(String searchStr) {
		List<NameValuePairVo> resultlist = new ArrayList<>();

		PigUser usr = SecurityUtils.getUser();
		Integer id = SecurityUtils.getUser().getId();
		//获取当前账号下 广告账户列表
		List<String> allList = this.getOwnerAdv(id, PlatformTypeEnum.TT.getValue());
//		List<AdUserAdver> adAccounts = adUserAdverService.list(Wrappers.<AdUserAdver>query().lambda().eq(AdUserAdver::getUserId, id)
//				.eq(AdUserAdver::getStatus, 0).eq(AdUserAdver::getPlatformId, PlatformTypeEnum.TT.getValue()));
//		List<String> adAccountList=adAccounts.stream().map(AdUserAdver::getAdvertiserId).collect(Collectors.toList());

		List<NameValuePairVo> listWithName = new ArrayList<>();
		allList.forEach(item -> {
			String name = stringRedisTemplate.opsForValue().get(Constants.AD_REDIS_AD_NAME_KEY_PRIX_ + item);
			String formatedName = String.format("%s(%s)", name, item);
			NameValuePairVo pair = new NameValuePairVo(formatedName, item);
			listWithName.add(pair);
		});


		List<NameValuePairVo> filterdList = null;
		if (StringUtils.isNotBlank(searchStr)){
			filterdList = listWithName.stream().filter(item -> item.getName().contains(searchStr)).collect(Collectors.toList());
		}else{
			filterdList = listWithName;
		}


		return filterdList;
	}

	/**修改日预算（定时修改日预算）
	 * @param budgetDto
	 * @return
     */
	@Override
	public R budgetUpdate(BudgetDto budgetDto) {
		try{
			if (null == budgetDto) {
				return R.failed( "param is empty");
			}
			String advertiserId=budgetDto.getAdvertiserId();
			if(StringUtils.isBlank(advertiserId)){
				return R.failed( "advertiserId is empty");
			}
			if(null == budgetDto.getType()){
				budgetDto.setType(StatusEnum.TYPE_ONE.getStatus());
			}
			AccountToken advertising =  advertiserService.getAdvertiserToken(PlatformTypeEnum.TT.getValue(),budgetDto.getAdvertiserId());
			if(null == advertising){
				return R.failed( "advertising account does not exist !");
			}


			if(StringUtils.isNotBlank(budgetDto.getBudgetMode())){



				if(budgetDto.getType().equals(StatusEnum.TYPE_ONE.getStatus())){
					//修改今日日限额
					if(StringUtils.equals(budgetDto.getBudgetMode(),"BUDGET_MODE_DAY")){
						//日限额
						if(null ==  budgetDto.getBudget()){
							return R.failed( "日预算不允许为空");
						}

					}
				}


			}
			BigDecimal budget=budgetDto.getBudget();
			if(null != budgetDto.getBudget()){
				Boolean flag= budgetDto.getBudget().compareTo(new BigDecimal(1000)) == -1   || budgetDto.getBudget().compareTo(new BigDecimal(9999999.99)) == 1 ;
				if(flag){
					return R.failed( "1000  <=  budget   <= 9999999.99");
				}
			}
			String budgetMode=budgetDto.getBudgetMode();
			if(StatusEnum.TYPE_ONE.getStatus().equals(budgetDto.getType())){
				//调用三方账号修改日预算
				if(StringUtils.isBlank(budgetMode)){
					return R.failed( "budgetMode is empty");
				}
				ConvertTrack convertTrack=new ConvertTrack();
				convertTrack.setAdAccount(advertiserId);
				String token= ttAccesstokenService.fetchAccesstoken(convertTrack.getAdAccount());       //convertTrackService.getAccesstoken(convertTrack);
				if(StringUtils.isBlank(token)){
					return R.failed( "advertising account does not exist !!");
				}
				//
				Map<String,Object> param=new HashMap<String,Object>();
				param.put("advertiser_id",advertiserId);
				param.put("budget_mode",budgetMode);
				param.put("budget",budget);
				String resultStr = OEHttpUtils.doPost(adThirdUrl + budgetUrl, param, token);
				//log.warn("budgetUpdate response:"+resultStr);
				JSONObject obj = JSON.parseObject(resultStr);
				String code = obj.getString("code");
				if (!StringUtils.equals(code,"0")){
					return R.failed( "advertising account update exception"+obj.get("message"));
				}
				advertiserMapper.update(new AdvertiserInfo(budgetDto.getAdvertiserId(),budgetDto.getBudgetMode(),budgetDto.getBudget(),new Date()));
			}else{
				//创建定时任务，执行定时任务修改日预算
				if(null == budgetDto.getBudget()){
					return R.failed( "budget is empty");
				}
				//添加日限额方式
				budgetDto.setBudgetMode("BUDGET_MODE_DAY");
//				String cronDate=null;
               //远程调用定时器项目创建定时任务
				try{
					//第二天凌晨执行
					Date exeTime= DateUtil.timeToBeginDay(DateUtil.getDate(new Date(),1));
					String  cronDate= CronUtils.getCron(exeTime);
					String exeParam =JSONObject.toJSONString(budgetDto);
					//一分钟后执行
//					cronDate=CronUtils.getCron(DateUtil.getMinuteDate(new Date(),-2));
                      /*(Long uniqueId,String jobDesc,String jobCron,Date executeTime,String executorParam,Integer triggerStatus,Integer operateType,Integer type)*/
					Long adId=Long.parseLong(advertiserId);
					List<PlatformJobInfo> platformJobInfoList = platformJobInfoMapper.selectList(Wrappers.<PlatformJobInfo>query().lambda().eq(PlatformJobInfo::getUniqueId,adId).eq(PlatformJobInfo::getOperateType,StatusEnum.OPERATE_TYPE_ONE.getStatus()).eq(PlatformJobInfo::getType,StatusEnum.AD_TYPE_ONE.getStatus()).gt(PlatformJobInfo::getExecuteTime, new Date()));
                     if(CollectionUtils.isEmpty(platformJobInfoList)){
						 //新增  -- 已经执行的以新增为标准，未执行以修改为准
						 platformJobInfoMapper.insert(new PlatformJobInfo(adId,"修改广告账号日预算",cronDate,exeTime,exeParam,StatusEnum.JOB_STATUS_ONE.getStatus(),StatusEnum.OPERATE_TYPE_ONE.getStatus(),StatusEnum.AD_TYPE_ONE.getStatus()));
					 }else{
						 //修改账号修改任务-----如果还没有执行
						 platformJobInfoMapper.updateById(new PlatformJobInfo(adId,exeParam,cronDate,exeTime,StatusEnum.JOB_STATUS_ONE.getStatus(),platformJobInfoList.get(0).getId()));
					 }
				}catch (Exception e){
					log.error("budgetUpdate  cron  is error:",e);
					return R.failed( "定时修改日预算异常，请稍后重试");

				}

			}

			return R.ok();

		}catch (Exception e){
			log.error("budgetUpdate is error",e);
			return R.failed( "advertising account update exception");
		}


	}




	/**
	 * 查询广告账号(广告账号，推广组，广告计划)预约当天定时设置预算值
	 * @param advertiserId
	 * @return
	 */
	@Override
	public R getAdvertiserJobBudget(Long advertiserId,Long campaignId,Long adId,Integer operateType) {
		Long unionId=null;
		if(null ==operateType|| StatusEnum.OPERATE_ONE.getStatus().equals(operateType)){
			if (null == advertiserId) {
				return R.failed("广告账号不允许为空");
			}
			operateType= StatusEnum.OPERATE_ONE.getStatus();
			unionId=advertiserId;
		}else if( StatusEnum.OPERATE_TWO.getStatus().equals(operateType)){
			if (null == campaignId) {
				return R.failed("推广组id不允许为空");
			}
			unionId=campaignId;
		}else if( StatusEnum.OPERATE_THREE.getStatus().equals(operateType)){
			if (null == adId) {
				return R.failed("广告计划id不允许为空");
			}
			unionId=adId;
		}

		BigDecimal bigDecimal=null;
		List<PlatformJobInfo> platformJobInfoList = platformJobInfoMapper.selectList(Wrappers.<PlatformJobInfo>query().lambda().eq(PlatformJobInfo::getUniqueId,unionId).eq(PlatformJobInfo::getOperateType,operateType).gt(PlatformJobInfo::getExecuteTime, new Date()));
		 if(CollectionUtils.isNotEmpty(platformJobInfoList)){
			 String exParam = platformJobInfoList.get(0).getExecutorParam();
			 BudgetDto 	budgetDto =null;

			 if (StringUtils.isNotBlank(exParam)) {
				 budgetDto = JSONObject.parseObject(exParam, BudgetDto.class);
				 bigDecimal= budgetDto.getBudget();
			 }
		 }
		return  R.ok(bigDecimal);
	}

	@Override
	public List<String> getOwnerAdv(Integer userId, String platformId) {
		List<String> advList = new ArrayList<>();
		if(userId == null) {
			return advList;
		}


		//为空则查询所有账号
		//为空则查询所有账号
		advList = adAccountMapper.selectList(Wrappers.<AdAccount>query().lambda().and(StringUtils.isNotBlank(platformId),
				wrapper -> wrapper.eq(AdAccount::getMediaCode, platformId)).and(wrapper -> wrapper.eq(AdAccount::getThrowUser, userId))).stream().map(AdAccount::getAdvertiserId).collect(Collectors.toList());
		return advList;
	}


	@Override
	public Map<String, String> getOwnerAdvMap(Integer userId, String platformId) {
		Map<String, String> map = new HashMap<>();
		List<String> list = this.getOwnerAdv(userId, platformId);
		list.forEach( a -> {
			map.put(a, stringRedisTemplate.opsForValue().get(Constants.AD_REDIS_AD_NAME_KEY_PRIX_ + a));
		});
		return map;
	}


	/**
	 * @可授权的账号列表(其他人不要用，这里专供审核页面使用)
	 * @return
	 */
	@Override
	public List<String> getAuthAdvList() {
		List<String> allList = new ArrayList<>();
		//自身含有的账号
		List<AdUserAdver> list = adUserAdverService.list(Wrappers.<AdUserAdver>query().lambda().eq(AdUserAdver::getUserId, SecurityUtils.getUser().getId())
				.eq(AdUserAdver::getStatus, 0));
		if(list == null || list.size() == 0) {
			return allList;
		}
		//转list
		List<String> adList=list.stream().map(AdUserAdver::getAdvertiserId).collect(Collectors.toList());
		//排除普通账号，查询出的都是管家账户
		List<String> housekeeperList = ttAccesstokenService.list(Wrappers.<TtAccesstoken>query().lambda().in(TtAccesstoken::getAdAccount, adList)).stream().map(TtAccesstoken::getAdAccount).collect(Collectors.toList());
		if(housekeeperList == null || housekeeperList.size() == 0) {
			return allList;
		}

		//账号管家名下的所有普通账号
		List<String> advList = advMapper.selectList(Wrappers.<Advertising>query().lambda().in(Advertising::getHousekeeper, adList)).stream().map(Advertising::getAdvertiserId).collect(Collectors.toList());

		//管家账号
		//allList.addAll(housekeeperList);
		allList.addAll(advList);

		return allList;
	}

	@Override
	public List<String> getThrowUserList() {
		//投放人集合
		List<String> throwUserList = Lists.newArrayList();
		//获取当前登入人所有的角色
		List<Integer> roles = SecurityUtils.getRoles();
		Integer userId = SecurityUtils.getUser().getId();
		//角色没有设置投放人，需要计算当前人是否关联了广告账户
		throwUserList.add(String.valueOf(userId));
		//管理员获取当前所有的账户
		if (!roles.contains(1)) {
			//非管理员
			//1.获取当前账户对应的角色 roles
			//2.获取角色关联的投放人-关联全部则与管理员权限一致
			R<List<AdRoleUser>> adRoleUserList = remoteAdRoleUserService.getUserListByRole(SecurityConstants.FROM_IN,roles);
			if (CollectionUtils.isNotEmpty(adRoleUserList.getData())) {
				List<Integer> userIds = adRoleUserList.getData().stream().distinct().map(AdRoleUser::getUserId).collect(Collectors.toList());
				//存在关联了全部投放人的角色则与管理员权限一致
				//userId = 0 表示关联全部
				if (!userIds.contains(0)) {
					List<String> userIdStr = userIds.stream().distinct().map(String::valueOf).collect(Collectors.toList());
					throwUserList.addAll(userIdStr);
				} else {
					throwUserList.clear();
				}
			} else {
				throwUserList.add("-1");
			}
		} else {
			throwUserList.clear();
		}
		return throwUserList;
	}

	@Override
	public List<AdAccount> getAccountList(Integer mediaType, String advertiserId) {
		//投放人集合
		List<String> throwUserList = this.getThrowUserList();
		LambdaQueryWrapper<AdAccount> query = new LambdaQueryWrapper<>();
		query.and((mediaType !=null && mediaType != 0 ),wrapper -> wrapper.eq(AdAccount::getMediaCode, String.valueOf(mediaType)));
		query.and(StringUtils.isNotBlank(advertiserId), wrapper -> wrapper.eq(AdAccount::getAdvertiserId, advertiserId).or().like(AdAccount::getAdvertiserName, advertiserId));
		query.and(CollectionUtils.isNotEmpty(throwUserList),wrapper -> wrapper.in(AdAccount::getThrowUser, throwUserList));
		//过滤管家账户
		query.apply("advertiser_id <> housekeeper");
		return adAccountMapper.selectList(query);
	}
}
